﻿using System;
using TrackSystem.IO.Loops;

namespace TrackSystem.IO.Utils
{
    /// <summary>
    /// PID参数自整定软件包  继电反馈整定算法
    /// </summary>
    public class AutoTunePackage
    {
        /// <summary>
        /// 信号稳定周期
        /// </summary>
        public const int LAG_PHASE = 5;

        #region 变量定义
        /// <summary>
        /// 整定控制开关 0-PID控制  1-参数整定 2-整定失败
        /// </summary>
        public int TuneEnabled { get; set; }
        /// <summary>
        /// 预处理使能，开始整定前置位
        /// </summary>
        public int PreEnabled { get; set; }
        /// <summary>
        /// 记录开始整定前偏差的初始状态
        /// </summary>
        public int InitialStatus { get; set; }
        /// <summary>
        /// 记录输出的初始状态，0允许上升过零计数；1允许下降过零计数
        /// </summary>
        public int OutputStatus { get; set; }
        /// <summary>
        /// 控制器类型：0，P控制器；1，PI控制器；2，PID控制器
        /// </summary>
        public int ControllerType { get; set; }
        /// <summary>
        /// 过零点计数器，每次输出改变加1，比实际过零次数多1
        /// </summary>
        public int ZeroAcrossCounter { get; set; }
        /// <summary>
        /// 上升迟滞时间计数器
        /// </summary>
        public int RiseLagCounter { get; set; }
        /// <summary>
        /// 下降迟滞时间计数器
        /// </summary>
        public int FallLagCounter { get; set; }
        /// <summary>
        /// 整定采样周期
        /// </summary>
        public int TunePeriod { get; set; }
        /// <summary>
        /// 整定计时器
        /// </summary>
        public int TuneTimer { get; set; }
        /// <summary>
        /// 记录波形周期起始时间
        /// </summary>
        public int StartTime { get; set; }
        /// <summary>
        /// 记录波形周期结束时间
        /// </summary>
        public int EndTime { get; set; }
        /// <summary>
        /// 输出阶跃d
        /// </summary>
        public double OutputStep { get; set; }
        /// <summary>
        /// 振荡波形中测量值的最大值
        /// </summary>
        public double MaxPV { get; set; }
        /// <summary>
        /// 振荡波形中测量值的最小值
        /// </summary>
        public double MinPV { get; set; }
        /// <summary>
        /// 调节器输出值
        /// </summary>
        public int MV { get; set; }

        /// <summary>
        /// 比例系数
        /// </summary>
        public double Kp { get; set; }
        /// <summary>
        /// 积分系数
        /// </summary>
        public double Ki { get; set; }
        /// <summary>
        /// 微分系数
        /// </summary>
        public double Kd { get; set; }

        /// <summary>
        /// 保存PID调节器
        /// </summary>
        private LoopItem loop;
        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="loop">PID环节</param>
        public AutoTunePackage(LoopItem loop)
        {
            this.loop = loop;
            Reset();
        }

        /// <summary>
        /// 参数复位
        /// </summary>
        public void Reset()
        {
            MaxPV =loop.SV + 0.1;
            MinPV = loop.SV - 0.1;
            OutputStep = (int)((loop.ULmt - loop.LLmt)/4);
            TuneTimer = 0;
            StartTime = 0;
            EndTime = 0;
            TunePeriod = 100;   //调节器循环周期为100ms

            if (loop.SV >= loop.PV)
            {
                InitialStatus = 1;   // 初始状态为1  设定值大于实际值
                OutputStatus = 0;    // 输出状态为0
            }
            else
            {
                InitialStatus = 0;  // 初始状态为1  设定值大于实际值
                OutputStatus = 1;   // 输出状态为0
            }

            ControllerType = 2;
            PreEnabled = 0;
            ZeroAcrossCounter = 0;
            RiseLagCounter = 0;
            FallLagCounter = 0;
            MV = 0;
        }

        /// <summary>
        /// 调节循环程序
        /// </summary>
        /// <returns></returns>
        public int RelayFeedbackAutoTuning()
        {
            TuneTimer++;
            int TuneDuration = TuneTimer*TunePeriod / 1000;
            if (TuneDuration > 3600)        //整定过程持续超过1小时，未能形成有效振荡，整定失败
            {
                TuneEnabled = 2;
                PreEnabled = 1;
                return 0;
            }

            if (loop.SV >= loop.PV) //设定值大于测量值，则开执行单元
            {
                RiseLagCounter++;   // 上升计数器打开
                FallLagCounter = 0; // 下降计数器置零

                if (RiseLagCounter > LAG_PHASE)  // 超过稳定时间
                {
                    MV = 1;       // 开启输出
                    if (OutputStatus == 0)   // 当前输出状态为0
                    {
                        OutputStatus = 1;    // 输出状态置1
                        ZeroAcrossCounter++; // 过零计数器递增

                        if (ZeroAcrossCounter == 3) //完成3次过零
                        {
                            StartTime = TuneTimer;  // 启动时间为当前计数器数值
                        }
                    }
                }
            }
            else     //设定值小于测量值，则关执行单元
            {
                FallLagCounter++;   // 下降计数器打开
                RiseLagCounter = 0; // 上升计数器置零

                if (FallLagCounter > LAG_PHASE) // 超过稳定时间
                {
                    MV = 0;  // 关闭输出
                    if (OutputStatus == 1)      // 当前输出状态为1
                    {
                        OutputStatus = 0;       // 输出状态置1
                        ZeroAcrossCounter++;    // 过零计数器递增

                        if (ZeroAcrossCounter == 3) //完成3次过零
                        {
                            StartTime = TuneTimer; // 启动时间为当前计数器数值
                        }
                    }
                }
            }

            if (ZeroAcrossCounter == 3)         //已经两次过零，可以记录波形数据
            {
                if (InitialStatus == 1)
                {
                    if (loop.PV < MinPV)        //初始设定值大于测量值，则区域3出现最小值
                    {
                        MinPV = loop.PV;
                    }
                }
                else if (InitialStatus == 0)    //初始设定值小于测量值，则区域3出现最大值
                {
                    if (loop.PV > MaxPV)
                    {
                        MaxPV = loop.PV;
                    }
                }
            }
            else if (ZeroAcrossCounter == 4)    //已经四次过零，记录另半波的数据
            {
                if (InitialStatus == 1)        //初始设定值大于测量值，则区域4出现最大值
                {
                    if (loop.PV > MaxPV)
                    {
                        MaxPV = loop.PV;
                    }
                }
                else if (InitialStatus == 0)    //初始设定值小于测量值，则区域4出现最小值
                {
                    if (loop.PV < MinPV)
                    {
                        MinPV = loop.PV;
                    }
                }
            }
            else if (ZeroAcrossCounter == 5)    //已经五次过零，振荡已形成可以整定参数
            {
                EndTime = TuneTimer;

                CalculationParameters();

                TuneEnabled = 2;
                PreEnabled = 1;
            }

            return MV;
        }

        /// <summary>
        /// 参数计算
        /// </summary>
        private void CalculationParameters()
        {
            double kc;
            double tc;
            double[][] zn = new double[3][];
            zn[0] = new double[3] { 0.5, 100000.0, 0.0 };
            zn[1] = new double[3] { 0.45, 0.8, 0.0 };
            zn[2] = new double[3] { 0.6, 0.5, 0.125 };

            tc = (EndTime - StartTime) * TunePeriod / 1000.0;
            kc = (8.0 * OutputStep) / (Math.PI * (MaxPV - MinPV));

            Kp = zn[ControllerType][0] * kc;
            Ki = Kp * TunePeriod / (zn[ControllerType][1] * tc)/100.0;
            Kd = Kp * zn[ControllerType][2] * tc / TunePeriod*10;
        }
    }
}
